import { Component } from "react";
import PropTypes from "prop-types";

// 官方react 提供 use
// import { use } from "react";

// 如果 getMessage 抛出错误,没有错误接收，会导致页面组件树奔溃
// 控制台错误 Consider adding an error boundary to your tree to customize error handling behavior.
const getMessage = Promise.reject("hello world");

// ErrorBoundary 不是现成的，是要自己写的，
class ErrorBoundary extends Component {
  constructor(props) {
    super(props);
    // 初始化状态
    this.state = {
      hasError: false,
      error: null,
    };
  }
  // 静态方法，捕获错误，用于从错误中派生出新的状态，修改this.state
  // 然后，执行render方法
  static getDerivedStateFromError(error) {
    return { hasError: true, error };
  }
  render() {
    // 有错误
    if (this.state.hasError) {
      return <div>出错被我捕获到了:{this.state.error}</div>;
    }
    // 'children' is missing in props validation,表示属性检查失败
    // 需要增加 ErrorBoundary.propTypes

    //没有错误，直接渲染子组件
    return this.props.children;
  }
}
// ErrorBoundary的propTypes，增加 children 必填，解决this.props.children报错
ErrorBoundary.propTypes = {
  children: PropTypes.node.isRequired,
};
function Message() {
  //使用use这个hook处理Promise并获取Promise的值
  const data = use(getMessage);
  return <div>{data}</div>;
}
function App() {
  return <Message />;
}
export default App;
// 自己实现use
function use(promise) {
  // 如果传入的不是promise, 则什么都不做, 直接返回参数的值
  if (!(promise instanceof Promise)) {
    return promise;
  }
  // 给promise,自定义status属性，根据改属性，进行不同处理

  // 第一次，进入的时候 promise.status为underfined, 会走入default
  // 向外抛一个promise，给App
  // 该promise,出结果之后，会再次渲染App
  // 再次进入use,进入 fulfilled 或者 reject
  switch (promise.status) {
    //如果Promise已经完成，则返回结果
    case "fulfilled":
      return promise.value;
    //如果Promise已经拒绝，抛出错误
    case "rejected":
      throw promise.reason;
    default:
      // throw,因为外面是用catch来捕获
      throw promise.then(
        //成功回调，设置状态为完成，并且保存结果
        (value) => {
          promise.status = "fulfilled";
          promise.value = value;
        },
        //失败回调，设置状态为拒绝，并且保存错误的原因
        (reason) => {
          promise.status = "rejected";
          promise.reason = reason;
        }
      );
  }
}
